summaryrefslogtreecommitdiff
path: root/app/[lng]/admin/edp/components/contract-items-form.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'app/[lng]/admin/edp/components/contract-items-form.tsx')
-rw-r--r--app/[lng]/admin/edp/components/contract-items-form.tsx230
1 files changed, 230 insertions, 0 deletions
diff --git a/app/[lng]/admin/edp/components/contract-items-form.tsx b/app/[lng]/admin/edp/components/contract-items-form.tsx
new file mode 100644
index 00000000..ef8f09ef
--- /dev/null
+++ b/app/[lng]/admin/edp/components/contract-items-form.tsx
@@ -0,0 +1,230 @@
+'use client'
+
+import { useState } from 'react'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
+
+import { toast } from 'sonner'
+import { createMultipleContractItems } from '../actions/contract-actions'
+import { ContractSelector } from './contract-selector'
+import { ItemSelector } from './item-selector'
+import { Item } from '../types/item'
+import { X } from 'lucide-react'
+
+interface Contract {
+ id: number
+ contractNo: string
+ contractName: string
+ status: string
+ projectId: number
+ vendorId: number
+ projectCode: string | null
+ projectName: string | null
+ vendorName: string | null
+ vendorCode: string | null
+}
+
+
+
+interface SelectedItem {
+ itemId: number
+ ProjectNo: string
+ itemName: string
+ packageCode: string
+ itemCode: string | null
+ description?: string
+ quantity?: number
+ unitPrice?: number
+}
+
+interface ContractItemsFormProps {
+ preselectedContractId?: number
+}
+
+export function ContractItemsForm({ preselectedContractId }: ContractItemsFormProps) {
+ const [loading, setLoading] = useState(false)
+ const [selectedContract, setSelectedContract] = useState<Contract | undefined>()
+ const [selectedItems, setSelectedItems] = useState<SelectedItem[]>([])
+ const [selectedItemIds, setSelectedItemIds] = useState<number[]>([])
+
+ // 아이템 선택 처리
+ const handleItemsSelect = (itemIds: number[], itemsData?: Item[]) => {
+ setSelectedItemIds(itemIds)
+
+ if (itemsData) {
+ // 새로운 아이템 데이터로 선택된 아이템 목록 업데이트
+ const newSelectedItems: SelectedItem[] = itemsData.map(item => {
+ // 기존 선택된 아이템에서 수량, 단가, 설명 정보 유지
+ const existing = selectedItems.find(selected => selected.itemId === item.id)
+ return {
+ itemId: item.id,
+ ProjectNo: item.ProjectNo,
+ itemName: item.itemName,
+ packageCode: item.packageCode,
+ itemCode: item.itemCode,
+ description: existing?.description || item.description || '',
+ quantity: existing?.quantity || 1,
+ unitPrice: existing?.unitPrice || 0
+ }
+ })
+ setSelectedItems(newSelectedItems)
+ } else {
+ // 기존 선택된 아이템들 중에서 새로 선택되지 않은 것들은 제거
+ setSelectedItems(prev => prev.filter(item => itemIds.includes(item.itemId)))
+ }
+ }
+
+ // 선택된 아이템 정보 반환 (이제 실제 데이터가 있음)
+ const getSelectedItemsWithDetails = (): SelectedItem[] => {
+ return selectedItems
+ }
+
+ // 선택된 아이템 정보 업데이트
+ const updateSelectedItem = (itemId: number, field: keyof SelectedItem, value: string | number) => {
+ setSelectedItems(prev => prev.map(item =>
+ item.itemId === itemId ? { ...item, [field]: value } : item
+ ))
+ }
+
+ // 선택된 아이템 제거
+ const removeSelectedItem = (itemId: number) => {
+ setSelectedItemIds(prev => prev.filter(id => id !== itemId))
+ setSelectedItems(prev => prev.filter(item => item.itemId !== itemId))
+ }
+
+ // 폼 제출
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+
+ if (!selectedContract) {
+ toast.error('계약을 선택해주세요.')
+ return
+ }
+
+ if (selectedItemIds.length === 0) {
+ toast.error('최소 하나의 아이템을 선택해주세요.')
+ return
+ }
+
+ setLoading(true)
+ try {
+ const currentSelectedItems = getSelectedItemsWithDetails()
+ const itemsData = currentSelectedItems.map(item => ({
+ itemId: item.itemId,
+ description: item.description,
+ quantity: item.quantity || 1,
+ unitPrice: item.unitPrice || 0
+ }))
+
+ const result = await createMultipleContractItems(selectedContract.id, itemsData)
+
+ if (result.success) {
+ toast.success(result.message)
+ setSelectedItems([])
+ setSelectedItemIds([])
+ } else {
+ toast.error(result.error)
+ }
+ } catch {
+ toast.error('계약 아이템 생성 중 오류가 발생했습니다.')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+ <Card>
+ <CardHeader>
+ <CardTitle>계약 아이템 생성</CardTitle>
+ <CardDescription>
+ 계약에 아이템들을 추가합니다.
+ </CardDescription>
+ </CardHeader>
+ <CardContent>
+ <form onSubmit={handleSubmit} className="space-y-6">
+ {/* 계약 선택 */}
+ <div>
+ <Label>계약 선택 *</Label>
+ <ContractSelector
+ selectedContract={selectedContract}
+ onContractSelect={setSelectedContract}
+ disabled={loading}
+ preselectedContractId={preselectedContractId}
+ />
+ </div>
+
+ {/* 선택된 아이템 목록 */}
+ {selectedItemIds.length > 0 && (
+ <div>
+ <Label>선택된 아이템 ({selectedItemIds.length}개)</Label>
+ <div className="mt-2 space-y-3 max-h-60 overflow-y-auto border rounded-md p-3">
+ {getSelectedItemsWithDetails().map(item => (
+ <div key={item.itemId} className="flex items-center gap-2 p-2 bg-gray-50 rounded">
+ <div className="flex-1">
+ <div className="font-medium text-sm">
+ {item.itemName} {item.itemCode && `(${item.itemCode})`}
+ </div>
+ <div className="text-xs text-muted-foreground">
+ 프로젝트: {item.ProjectNo} | 패키지: {item.packageCode}
+ </div>
+ <div className="flex gap-2 mt-1">
+ <Input
+ type="number"
+ placeholder="수량"
+ value={item.quantity || ''}
+ onChange={(e) => updateSelectedItem(item.itemId, 'quantity', parseInt(e.target.value) || 1)}
+ className="w-20 h-8 text-xs"
+ />
+ <Input
+ type="number"
+ placeholder="단가"
+ value={item.unitPrice || ''}
+ onChange={(e) => updateSelectedItem(item.itemId, 'unitPrice', parseFloat(e.target.value) || 0)}
+ className="w-24 h-8 text-xs"
+ />
+ <Input
+ type="text"
+ placeholder="설명"
+ value={item.description || ''}
+ onChange={(e) => updateSelectedItem(item.itemId, 'description', e.target.value)}
+ className="flex-1 h-8 text-xs"
+ />
+ </div>
+ </div>
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => removeSelectedItem(item.itemId)}
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
+
+ {/* 아이템 선택 */}
+ <div>
+ <Label>아이템 선택</Label>
+ <div className="mt-1">
+ <ItemSelector
+ selectedItems={selectedItemIds}
+ onItemsSelect={handleItemsSelect}
+ disabled={loading}
+ />
+ </div>
+ </div>
+
+ <Button type="submit" disabled={loading || selectedItemIds.length === 0} className="w-full">
+ {loading ? '생성 중...' : `선택된 ${selectedItemIds.length}개 아이템 추가`}
+ </Button>
+ </form>
+ </CardContent>
+ </Card>
+ )
+}